// 版权归Go作者所有。版权所有。
// 此源代码的使用受BSD样式的约束
// 可以在许可证文件中找到的许可证。

// 复制自Go发行版src/Go/build/build.Go，syslist.Go

package imports

import (
	"bytes"
	"strings"
	"unicode"
)

var slashslash = []byte("// ")

// ShouldBuild报告是否可以使用此文件，
// 规则是在文件的前导运行
// 和空行，后面必须跟一个空行
// （避免包含Go程序包条款文档注释），
// 以'
// None
// 只有当每一行都列出了某些内容时，才接受该文件
// 匹配文件。例如：
// None
// None
// None
// 将文件标记为仅适用于Windows和Linux。
// None
// 如果标签[*]是真的，那么SubdBube将考虑每一个
// 除了“ignore”之外的build标记对于
// 满足构建标记的目的是为了估计
// （保守地）一个文件是否可能被使用
// 在任何建造中。
// None
func ShouldBuild(content []byte, tags map[string]bool) bool {
	// 通过1。确定主要运行的
	// 后面必须有一个空行。
	end := 0
	p := content
	for len(p) > 0 {
		line := p
		if i := bytes.IndexByte(line, '\n'); i >= 0 {
			line, p = line[:i], p[i+1:]
		} else {
			p = p[len(p):]
		}
		line = bytes.TrimSpace(line)
		if len(line) == 0 { // 空行
			end = len(content) - len(p)
			continue
		}
		if !bytes.HasPrefix(line, slashslash) { // 不评论行
			break
		}
	}
	content = content[:end]

	// 通过2。处理运行中的每一行。
	p = content
	allok := true
	for len(p) > 0 {
		line := p
		if i := bytes.IndexByte(line, '\n'); i >= 0 {
			line, p = line[:i], p[i+1:]
		} else {
			p = p[len(p):]
		}
		line = bytes.TrimSpace(line)
		if !bytes.HasPrefix(line, slashslash) {
			continue
		}
		line = bytes.TrimSpace(line[len(slashslash):])
		if len(line) > 0 && line[0] == '+' {
			// 看起来像一条注释+行。
			f := strings.Fields(string(line))
			if f[0] == "+build" {
				ok := false
				for _, tok := range f[1:] {
					if matchTags(tok, tags) {
						ok = true
					}
				}
				if !ok {
					allok = false
				}
			}
		}
	}

	return allok
}

// matchTags报告名称是否为以下之一：
// None
// 标记（如果标记[tag]为真）
// !标记（如果标记[tag]为false）
// 以逗号分隔的列表
// None
func matchTags(name string, tags map[string]bool) bool {
	if name == "" {
		return false
	}
	if i := strings.Index(name, ","); i >= 0 {
		// 逗号分隔列表
		ok1 := matchTags(name[:i], tags)
		ok2 := matchTags(name[i+1:], tags)
		return ok1 && ok2
	}
	if strings.HasPrefix(name, "!!") { // 语法错误，始终拒绝
		return false
	}
	if strings.HasPrefix(name, "!") { // 反面
		return len(name) > 1 && matchTag(name[1:], tags, false)
	}
	return matchTag(name, tags, true)
}

// matchTag报告标记名是否有效以及标记[name]==want是否满足。
func matchTag(name string, tags map[string]bool, want bool) bool {
	// 标签必须是字母、数字、下划线或点。
	// 与Go标识符不同，所有数字都很好（例如，“386”）。
	for _, c := range name {
		if !unicode.IsLetter(c) && !unicode.IsDigit(c) && c != '_' && c != '.' {
			return false
		}
	}

	if tags["*"] && name != "" && name != "ignore" {
		// 收集所有可能进口的特殊情况：
		// 如果我们把*放在标签映射中，那么所有标签
		// “忽略”除外，视为既存在又不存在
		// （因此，无论如何设置“want”，我们都会返回true）。
		return true
	}

	have := tags[name]
	if name == "linux" {
		have = have || tags["android"]
	}
	if name == "solaris" {
		have = have || tags["illumos"]
	}
	if name == "darwin" {
		have = have || tags["ios"]
	}
	return have == want
}

// 如果名称包含$GOOS或$GOARCH，则MatchFile返回false
// 与当前系统不匹配的后缀。
// 可识别的名称格式包括：
// None
// 名称$（GOOS）*
// 名称$（GOARCH）*
// 名称$（GOOS）$（GOARCH）*
// 名称$（GOOS）\u测试*
// 名称$（GOARCH）\u测试*
// 名称$（GOOS）$（GOARCH）\u测试*
// None
// 例外情况：
// 如果GOOS=android，那么也会匹配GOOS=linux的文件。
// 如果GOOS=illumos，那么也会匹配GOOS=solaris的文件。
// 如果GOOS=ios，那么也会匹配GOOS=darwin的文件。
// None
// 如果标签[*]是真的，那么MatCHFLID将考虑所有可能的情况。
// GOOS和GOARCH将可用，因此
// 始终返回true。
func MatchFile(name string, tags map[string]bool) bool {
	if tags["*"] {
		return true
	}
	if dot := strings.Index(name, "."); dot != -1 {
		name = name[:dot]
	}

	// 在Go 1.4之前，一个名为“linux.Go”的文件相当于一个
	// 在该文件中构建标记“linux”。对于Go 1.4及更高版本，我们需要
	// 自动标记仅应用于具有非空前缀的文件，因此
	// “foo_linux.go”已标记，但“linux.go”未标记。这允许新的操作系统
	// 系统，如android，在不破坏现有代码的情况下
	// “android.go”中的无害源代码。最简单的解决办法：削减一切
	// 在首字母前的名称中。
	i := strings.Index(name, "_")
	if i < 0 {
		return true
	}
	name = name[i:] // 先不理一切，然后再开始_

	l := strings.Split(name, "_")
	if n := len(l); n > 0 && l[n-1] == "test" {
		l = l[:n-1]
	}
	n := len(l)
	if n >= 2 && KnownOS[l[n-2]] && KnownArch[l[n-1]] {
		return matchTag(l[n-2], tags, true) && matchTag(l[n-1], tags, true)
	}
	if n >= 1 && KnownOS[l[n-1]] {
		return matchTag(l[n-1], tags, true)
	}
	if n >= 1 && KnownArch[l[n-1]] {
		return matchTag(l[n-1], tags, true)
	}
	return true
}

var KnownOS = map[string]bool{
	"aix":       true,
	"android":   true,
	"darwin":    true,
	"dragonfly": true,
	"freebsd":   true,
	"hurd":      true,
	"illumos":   true,
	"ios":       true,
	"js":        true,
	"linux":     true,
	"nacl":      true, // 遗产不要移除
	"netbsd":    true,
	"openbsd":   true,
	"plan9":     true,
	"solaris":   true,
	"windows":   true,
	"zos":       true,
}

var KnownArch = map[string]bool{
	"386":         true,
	"amd64":       true,
	"amd64p32":    true, // 遗产不要移除
	"arm":         true,
	"armbe":       true,
	"arm64":       true,
	"arm64be":     true,
	"ppc64":       true,
	"ppc64le":     true,
	"mips":        true,
	"mipsle":      true,
	"mips64":      true,
	"mips64le":    true,
	"mips64p32":   true,
	"mips64p32le": true,
	"ppc":         true,
	"riscv":       true,
	"riscv64":     true,
	"s390":        true,
	"s390x":       true,
	"sparc":       true,
	"sparc64":     true,
	"wasm":        true,
}
